package path.calculator;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class PathCalculatorTest {
    private PathCalculator calculator;
    private long startTime;

    @Test
    public void noNodesNoPaths() {
        calculator = new PathCalculator(0);
        assertEquals(0, calculator.totalPaths());
    }

    @Test
    public void oneNodeResultsInOnePossiblePath() {
        calculator = new PathCalculator(1);
        int totalPaths = calculator.totalPaths();
        assertEquals(1, totalPaths);

    }

    @Test
    public void threeNodesNoDependencies6Paths() {
        calculator = new PathCalculator(3);
        int totalPaths = calculator.totalPaths();
        assertEquals(6, totalPaths);
    }

    @Test
    public void fourNodesNoDependencies24Paths() {
        calculator = new PathCalculator(4);
        int totalPaths = calculator.totalPaths();
        assertEquals(24, totalPaths);
    }

    @Test
    public void twoNodesOneDependencyOnePath() {
        calculator = new PathCalculator(2);
        createLine('a', 'b');
        int totalPaths = calculator.totalPaths();
        assertEquals(1, totalPaths);
    }

    @Test
    public void threeNodesInALineOnePath() {
        calculator = new PathCalculator(3);
        createLine('a', 'b');
        createLine('b', 'c');
        int totalPaths = calculator.totalPaths();
        assertEquals(1, totalPaths);
    }

    @Test
    public void fourNodesWithToIndependentDependneciesShouldResultInSixPahts() {
        calculator = new PathCalculator(4);
        createLine('a', 'b');
        createLine('c', 'd');
        int totalPaths = calculator.totalPaths();
        assertEquals(6, totalPaths);
    }

    @Test
    public void twoNodesWithFlowInBothDirectionsShouldResultInNoPaths() {
        calculator = new PathCalculator(2);
        createLine('a', 'b');
        createLine('b', 'a');
        int totalPaths = calculator.totalPaths();
        assertEquals(0, totalPaths);
    }

    @Test
    public void whatIsThreeThreadsTwoStepsEach() {
        calculator = new PathCalculator(6);
        createLine('a', 'b');
        createLine('c', 'd');
        createLine('e', 'f');
        start();
        int totalPaths = calculator.totalPaths();
        stop(3, 2, totalPaths);
    }

    @Test
    public void whatIs2ThreadsAndThreeSteps() {
        calculator = new PathCalculator(6);
        createLine('a', 'b', 'c');
        createLine('d', 'e', 'f');
        start();
        int totalPaths = calculator.totalPaths();
        stop(2, 3, totalPaths);
    }

    @Test
    public void whatIs4Threads2Opcodes() {
        calculator = new PathCalculator(8);
        createLine('a', 'b');
        createLine('c', 'd');
        createLine('e', 'f');
        createLine('g', 'h');
        start();
        int totalPaths = calculator.totalPaths();
        stop(4, 2, totalPaths);
    }

    @Test
    public void twoThreadsOneSSteps() {
        calculator = new PathCalculator(2);
        start();
        int totalPaths = calculator.totalPaths();
        stop(2, 5, totalPaths);
    }

    @Test
    public void twoNodesResultsIn2Paths() {
        calculator = new PathCalculator(2);
        int totalPaths = calculator.totalPaths();
        assertEquals(2, totalPaths);
    }

    @Test
    public void twoThreadsFourSteps() {
        calculator = new PathCalculator(8);
        createLine('a', 'b', 'c', 'd');
        createLine('e', 'f', 'g', 'h');
        start();
        int totalPaths = calculator.totalPaths();
        stop(2, 4, totalPaths);
    }

    @Test
    public void twoThreadsFiveSteps() {
        calculator = new PathCalculator(10);
        createLine('a', 'b', 'c', 'd', 'e');
        createLine('f', 'g', 'h', 'i', 'j');
        start();
        int totalPaths = calculator.totalPaths();
        stop(2, 5, totalPaths);
    }

    @Test
    public void whatIs2threadsAnd6Steps() {
        calculator = new PathCalculator(12);
        createLine('a', 'b', 'c', 'd', 'e', 'f');
        createLine('g', 'h', 'i', 'j', 'k', 'l');
        start();
        int totalPaths = calculator.totalPaths();
        stop(2, 6, totalPaths);
    }

    @Test
    public void whatIs2ThreadsAnd7Steps() {
        calculator = new PathCalculator(14);
        createLine('a', 'b', 'c', 'd', 'e', 'f');
        createLine('g', 'h', 'i', 'j', 'k', 'l');
        start();
        int totalPaths = calculator.totalPaths();
        stop(2, 7, totalPaths);
    }

    @Test
    public void whatIs10ThreadsOneOpcode() {
        calculator = new PathCalculator(10);
        start();
        int totalPaths = calculator.totalPaths();
        stop(10, 1, totalPaths);
    }

    private void stop(int threads, int opcodes, int totalPaths) {
        System.out.printf("Time: %8dms -- Threads: %2d, Opcodes: %2d, Total paths: %7d\n", System
                .currentTimeMillis()
                - startTime, threads, opcodes, totalPaths);
    }

    private void start() {
        startTime = System.currentTimeMillis();
    }

    private void createLine(char... values) {
        for (int i = 0; i < values.length - 1; ++i)
            calculator.mustFlowFromNodeToNode(values[i], values[i + 1]);
    }

}
